package game.jabberserver;

import game.MainGame.ServerGUI;
import game.jabberclient.Message;
import java.net.Socket;
import java.util.ArrayList;
import java.io.*;

/**
 * Communicates with clients.
 * 
 */
public class ClientSession extends Thread {

	private Socket socket;

	public Socket getSocket() {
		return socket;
	}

	private ObjectInputStream netIn;

	public ObjectInputStream getNetIn() {
		return netIn;
	}

	private ObjectOutputStream netOut;

	public ObjectOutputStream getNetOut() {
		return netOut;
	}

	private String username;
	private Server server;
	private Message message;
	private ServerGUI gui;
	private int clientId;
	private int buttonIndex = 1;
	private boolean enter = true;

	public ClientSession(Socket s, Server server) {
		this.socket = s;
		this.server = server;
		gui = server.getServerGUI();
		try {
			netOut = new ObjectOutputStream(socket.getOutputStream());
			netIn = new ObjectInputStream(socket.getInputStream());
			username = (String) netIn.readObject();
			clientId = (int) server.getID();
			username = clientId + " " + username;
			server.getClients().add(this); // adds new communications to server.
			broadcastAllNames();
			server.display(username + " just connected.");
		} catch (Exception e) {
			server.display("Exception creating new Input/output Streams: " + e);
			return;
		}
		start();
	}

	/**
	 * Adds new players to the list on the client GUI.
	 */
	public void broadcastAllNames() {
		try {
			for (int i = server.getClients().size(); --i >= 0;) {
				ClientSession client = server.getClients().get(i);
				client.netOut.writeObject(new Message(Message.NEWPL, username));
			}
			if (server.getClients().size() > 1) {
				for (int i = server.getClients().size() - 1; --i >= 0;) {
					netOut.writeObject(new Message(Message.NEWPL, server
							.getClients().get(i).username));
					if(server.isStarted()){
						netOut.writeObject(new Message(Message.START, ""));
					}
					
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Receives messages from client and sends messages to client.
	 */
	public void run() {
		boolean keepGoing = true;
		while (keepGoing) {
			try {
				message = (Message) netIn.readObject();
			} catch (IOException e) {
				server.display(username + " Exception reading Streams: " + e);
				break;
			} catch (ClassNotFoundException e) {
				break;
			}
			String msg = message.getMessage();
			switch (message.getType()) {
			case Message.VICTORY:
				if (!server.isSomeonWon()) {
					broadcastAll(new Message(Message.VICTORY, username + " "
							+ buttonIndex + " victorious"));
					server.setSomeonWon(true);
				} else {
					broadcastAll(new Message(Message.VICTORY, username + " "
							+ buttonIndex + " not"));
				}
				remove(this);
				break;
			case Message.NEXT:
				server.setPlayerMove(server.getPlayerMove());
				int i = server.getPlayerMove();
				ClientSession client = server.getPlaying().get(i);
				try {
					client.netOut.writeObject(new Message(Message.TURN, ""));
				} catch (IOException e) {
					server.display("Error in turns");
				}
				break;
			case Message.START:
				if (!server.isStarted()) {
					if (server.getClients() != null) {
						server.setPlaying(server.getClients());
						try {
							server.getPlaying().get(0).netOut
									.writeObject(new Message(Message.TURN, ""));
						} catch (IOException e) {
						}
					}
					
					broadcastAll(new Message(Message.MESSAGE, "Player "
							+ username + " has started the game. Have fun!"));
					broadcastAll(new Message(Message.START, ""));
					
					server.setStarted(true);
				}
				break;
			case Message.MESSAGE:
				broadcast(msg);
				broadcastAll(message);
				break;
			case Message.LOGOUT:
				server.display(username
						+ " disconnected with a LOGOUT message.");
				broadcastAll(new Message(Message.LOGOUT, msg + " " + username));
				keepGoing = false;
				break;
			case Message.DICE:
				server.display(msg);
				if (enter) {
					diceMoves(message);
				}
				broadcastAll(new Message(Message.MESSAGE, msg));
				break;
			case Message.MOVE:
				server.display(msg);
				String[] helper = msg.split(" ");
				if (Integer.parseInt(helper[0]) == clientId) {
					int location = Integer.parseInt(helper[1]);
					buttonIndex = location;
				}
				broadcastAll(message);
				break;
			case Message.IN:
				enter = false;
				break;
			}
		}
		remove(this);
		close();
	}

	/**
	 * Sends message of other clients current position to current client
	 * connected with this ClientSession after his/her first move.
	 * 
	 */
	private void diceMoves(Message message) {
		for (int i = server.getClients().size(); --i >= 0;) {
			ClientSession client = server.getClients().get(i);
			try {
				netOut.writeObject(new Message(Message.MOVE, client.clientId
						+ " " + client.buttonIndex));
			} catch (Exception e) {
				server.display("Imposssible to send");
			}

		}
		enter = false;
	}

	/**
	 * Sends message to everyone.
	 * 
	 * @param message
	 */
	private void broadcastAll(Message message) {
		try {
			for (ClientSession client : server.getClients()) {
				client.netOut.writeObject(message);
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Removes clientSession from server list. This means that client has either
	 * left or won the game.
	 * 
	 * @param cli
	 *            ClientSession, which to be removed.
	 */
	synchronized void remove(ClientSession cli) {
		ArrayList<ClientSession> clients = server.getClients();
		for (int i = 0; i < clients.size(); ++i) {
			ClientSession client = clients.get(i);
			if (cli == client) {
				clients.remove(i);
				return;
			}
		}
	}

	void close() {
		try {
			if (netOut != null)
				netOut.close();
		} catch (Exception e) {
		}
		try {
			if (netIn != null)
				netIn.close();
		} catch (Exception e) {
		}
		try {
			if (socket != null)
				socket.close();

		} catch (Exception e) {
		}
	}

	/**
	 * Forwars message to ServerGUI chat JTextArea.
	 * 
	 * @param message
	 *            Message string.
	 */
	private synchronized void broadcast(String message) {
		String messageLf = message + "\n";
		gui.appendRoom(messageLf); // append in the room window
	}

}
